﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace TileMap
{
    class HeightMapDownLoader
    {
        private static readonly double SourceResolution = 90;
        private static readonly string SaveDirectory = "HeightMap";

        public static void DownLoad(TileMapData res)
        {
            if (res == null && res.CellDatas == null)
            {
                return;
            }

            TencentCoordToWGS84(res);

            DirectoryInfo info = new DirectoryInfo(SaveDirectory);
            if (info.Exists)
            {
                info.Delete(true);
                while (info.Exists)
                {
                    Thread.Sleep(100);
                    info.Refresh();
                }
            }
            while (!info.Exists)
            {
                info.Create();
                Thread.Sleep(100);
                info.Refresh();
            }

            var resolution = res.CellHeight / SourceResolution;
            int mapResolution = 513;
            for (int i = 0; i < 10; ++i)
            {
                double s = Math.Pow(2, i);
                if (s > resolution)
                {
                    mapResolution = (int)s + 1;
                    break;
                }
            }

            List<TileMapCellData> datas = new List<TileMapCellData>();

            for (int c = 0; c < res.CellColumnCount; ++c)
            {
                for (int r = 0; r < res.CellRowCount; ++r)
                {
                    TileMapCellData data = res.CellDatas[r, c];
                    data.HeightMapResolution = mapResolution;
                    data.HeightMapFileName = SaveDirectory + "/" + string.Format("HeightMap_{0}_{1}.dat", r, c);
                    datas.Add(data);
                }
            }

            Parallel.ForEach(datas,
                new ParallelOptions()
                {
                    MaxDegreeOfParallelism = 8
                },
                x => DownLoadMap(x));
        }

         private static void DownLoadMap(TileMapCellData x)
        {
            int resolution = x.HeightMapResolution;
            TileMapCellHeightData data = new TileMapCellHeightData
            {
                Data = new float[resolution, resolution]
            };

            TencentCoordToWGS84(x);

            Parallel.For(0, resolution * resolution, i =>
            {

                data.Data[i % resolution, i / resolution] = GetHeight(x, resolution, i / resolution, i % resolution);
            });

            // find max and min height
            float min = 8000.0f, max = -1000.0f;
            foreach (var h in data.Data)
            {
                max = Math.Max(max, h);
                min = Math.Min(min, h);
            }

            // scale data
            Parallel.For(0, resolution * resolution, i =>
            {
                var f = data.Data[i / resolution, i % resolution];
                f = (f - min) / (max - min);
                data.Data[i / resolution, i % resolution] = f;
            });

            x.MaxHeight = max;
            x.MinHeight = min;

            data.Serialize(x.HeightMapFileName);

            // For Test
            //var test = TileMapCellHeightData.Deserialize(x.HeightMapFileName);
            //Debug.Assert(test.Data.Length == data.Data.Length);

            data.Data = null;
            data = null;
        }

        private static void TencentCoordToWGS84(TileMapData res)
        {
            CoordTransform coord = new CoordTransform();
            var tl = coord.Gcj02ToWgs84(new Coordinate(res.Left, res.Top));
            var br = coord.Gcj02ToWgs84(new Coordinate(res.Right, res.Bottom));
            res.Left = tl.Lon;
            res.Right = br.Lon;
            res.Top = tl.Lat;
            res.Bottom = br.Lat;
        }

        private static void TencentCoordToWGS84(TileMapCellData data)
        {
            CoordTransform coord = new CoordTransform();
            var tl = coord.Gcj02ToWgs84(new Coordinate(data.Left, data.Top));
            var br = coord.Gcj02ToWgs84(new Coordinate(data.Right, data.Bottom));
            data.Left = tl.Lon;
            data.Right = br.Lon;
            data.Top = tl.Lat;
            data.Bottom = br.Lat;
        }

        private static float GetHeight(TileMapCellData x, int resolution, int row, int column)
        {
            double lon = x.Left + (x.Right - x.Left) * row / (resolution - 1);
            double lat = x.Bottom + (x.Top - x.Bottom) * column / (resolution - 1);
            return TifHeightFile.GetHeight(lon, lat);
        }
    }
}
